Ext.override(Ext.LoadMask, {
    onBeforeLoad: function() {
        if (!this.disabled) {
            this.loading = true;
        }
 
        this.callOverridden(arguments);
    }
});

// EXTJSIV-3770 - scrolling for trees sometimes gets stuck
Ext.override(Ext.grid.Scroller, {
  afterRender: function() {
    var me = this;
    me.callParent();
    
    me.mon(me.scrollEl, 'scroll', me.onElScroll, me);
    
    Ext.cache[me.el.id].skipGarbageCollection = true;
    
    // add another scroll event listener to check, if main listeners is active
    Ext.EventManager.addListener(me.scrollEl, 'scroll', me.onElScrollCheck, me);
    
    // ensure this listener doesn't get removed
    Ext.cache[me.scrollEl.id].skipGarbageCollection = true;
  },

  // flag to check, if main listeners is active
  wasScrolled: false,

  // synchronize the scroller with the bound gridviews
  onElScroll: function(event, target) {
    this.wasScrolled = true; // change flag -> show that listener is alive
    this.fireEvent('bodyscroll', event, target);
  },

  // executes just after main scroll event listener and check flag state
  onElScrollCheck: function(event, target, options) {
    var me = this;

    if (!me.wasScrolled) {
      // Achtung! Event listener was disappeared, so we'll add it again
      me.mon(me.scrollEl, 'scroll', me.onElScroll, me);
    }
    
    me.wasScrolled = false; // change flag to initial value
  }
});

Ext.override(Ext.data.TreeStore, {
    load: function(options) {
        options = options || {};
        options.params = options.params || {};
 
        var me = this,
            node = options.node || me.tree.getRootNode(),
            root;
 
        // If there is not a node it means the user hasnt defined a rootnode yet. In this case lets just
        // create one for them.
        if (!node) {
            node = me.setRootNode({
                expanded: true
            });
        }
 
        if (me.clearOnLoad) {
            // this is what we changed.  added false
            node.removeAll(false);
        }
 
        Ext.applyIf(options, {
            node: node
        });
        options.params[me.nodeParam] = node ? node.getId() : 'root';
 
        if (node) {
            node.set('loading', true);
        }
 
        return me.callParent([options]);
    }
});

Ext.override(Ext.menu.Item, {
	deferExpandMenu: function() {
		var me = this;

		if (!me.menu.rendered || !me.menu.isVisible()) {
			me.parentMenu.activeChild = me.menu;
			me.menu.parentItem = me;
			me.menu.parentMenu = me.menu.ownerCt = me.parentMenu;
			me.menu.showBy(me, me.menuAlign);
						
			// EXTJSIV-4481: sub-menus are sometimes not aligned. run it again to get re-aligned.
			me.menu.showBy(me, me.menuAlign);						
		}
	}
});

Ext.override(Ext.form.Basic, {
    isDirty: function() {
        return !!this.getFields().findBy(function(f) {
            if(f.isDirty){
                return f.isDirty();
            }
        });
    },
});

Ext.override(Ext.AbstractComponent, {
	stateful: false
});

Ext.layout.container.AbstractCard.override({    
    deferredRender: true // tab and card layouts were painfully slow - their deferrment was being ignored. 
});

// prevent isOnLeftEdge js errors
Ext.grid.column.Column.override({
    initResizable: Ext.emptyFn
});

Ext.override(Ext.selection.RowModel, {
    onLastFocusChanged: function(oldFocused, newFocused, supressFocus) {        
        var views   = this.views || [this.view], // overridden - check for this.views
            viewsLn = views.length,
            store   = this.store,
            rowIdx,
            i = 0;

        if (oldFocused) {
            rowIdx = store.indexOf(oldFocused);
            if (rowIdx != -1) {
                for (; i < viewsLn; i++) {
                    views[i].onRowFocus(rowIdx, false);
                }
            }
        }

        if (newFocused) {
            rowIdx = store.indexOf(newFocused);
            if (rowIdx != -1) {
                for (i = 0; i < viewsLn; i++) {
                    views[i].onRowFocus(rowIdx, true, supressFocus);
                }
            }
        }
    }    
});

Ext.override(Ext.Img, {
    setAlt: function(text) {
        this.el.set({'data-qtip': text});
    }    
});

// revive and convert some 3.x existing methods that are useful
Ext.override(Ext.AbstractComponent, {
    find : function(attributeName, attributeValue){
        return Ext.ComponentQuery.query('component['+attributeName+'="'+attributeValue+'"]', this);
    },
    
    findByType: function(xtype){
        return Ext.ComponentQuery.query(xtype, this);
    }   
});

/**
 * additional functionality to bind a field to a particular meta field.
 */
Ext.override(Ext.Component, {
	/**
	 * sets the object name using the object name attribute and if not set, then it uses the parentObjectName.  Since these controls
	 * are composite, we always consider the parent object name, if this container has not specifically specified an object name.
	 */
	initializeObjectName: function(){
		if (!this.objectName) {
			if (!this.parentObjectName) {
				Ext.Msg.show({
					title: 'Cannot connect to a meta object',
					msg: 'A KContainer requires either an objectName or a parentObjectName in order to connect to a meta object.',
					buttons: Ext.Msg.OK,
					icon: Ext.Msg.ERROR
				});
				return false;
			}
			else {
				this.objectName = this.parentObjectName;
			}
		}
	},
	
	initializeMetaComponents: function(){
		if (this.metaComponents) {
			for (var key in this.metaComponents) {
				if (Ext.isObject(this.metaComponents[key]) && this.metaComponents[key].MetaObjectType) {
					// initialize meta object attributes
					this.metaComponents[key].MetaObjectType.enableAdvancedFilter = this.toBoolAttribute(this.metaComponents[key].MetaObjectType.enableAdvancedFilter);
					this.metaComponents[key].MetaObjectType.isTopLevel = this.toBoolAttribute(this.metaComponents[key].MetaObjectType.isTopLevel);
					this.metaComponents[key].MetaObjectType.enableAdvancedFilterSearch = this.toBoolAttribute(this.metaComponents[key].MetaObjectType.enableAdvancedFilterSearch);
					
					this.initializeMetaFields(this.metaComponents[key].MetaObjectType, false);
				}
			}
			
			if (this.metaComponents.length == 1 && this.metaComponents[0].MetaObjectType) {
				this.metaComponents[0].MetaObjectType.isTopLevel = true;
			}
		}
	},
	
	initializeMetaFields: function(_obj, _isField){
		if (_isField) {
			this.initializeMetaField(_obj);
		}
		else {
			for (var t in _obj) {
				if (Ext.isArray(_obj[t]) && t.endsWith('Field')) {
					for (var a = 0; a < _obj[t].length; a++) {
						this.initializeMetaFields(_obj[t][a], true);
					}
				}
				else 
					if (Ext.isObject(_obj[t]) && t.endsWith('Field')) {
						this.initializeMetaField(_obj[t]);
					}
			}
		}
	},
	
	initializeMetaField: function(_obj){
		// correct bool attributes that are coming accross as strings
		_obj.fixedColumnWidth = this.toBoolAttribute(_obj.fixedColumnWidth);
		_obj.dimensionable = this.toBoolAttribute(_obj.dimensionable);
		_obj.hidden = this.toBoolAttribute(_obj.hidden);
		_obj.visible = this.toBoolAttribute(_obj.visible);
		_obj.required = this.toBoolAttribute(_obj.required);
		_obj.sortable = this.toBoolAttribute(_obj.sortable);
		_obj.orderedData = this.toBoolAttribute(_obj.orderedData);		
		_obj.collapsible = this.toBoolAttribute(_obj.collapsible);
	},
	
	/**
	 * Gives an opportunity to alter the configuration on initialization.  the
	 * primary usage is to set the xtype to a more specific xtype based on some of its internal composition.
	 * @param {Object} _configuration the field or container to be initialized.
	 */
	initializeSubControl: function(_configuration, _objType){
		// if the sub control has a px widthUnit then set the width to a number instead of a string.  a string is interpreted as a percent
		if (_configuration.widthUnit && _configuration.widthUnit == 'PX') {
			if (parseInt(_configuration.width)) {
				_configuration.width = parseInt(_configuration.width);
			}
		}
		
		// if the sub control has a px heightUnit then set the height to a number instead of a string.  a string is interpreted as a percent
		if (_configuration.heightUnit && _configuration.heightUnit == 'PX') {
			if (parseInt(_configuration.height)) {
				_configuration.height = parseInt(_configuration.height);
			}
		}
		
		var metaField;
		var oType = _objType;
		if (oType.lastIndexOf('_') > -1) {
			oType = oType.substring(0, oType.lastIndexOf('_'));
		}
		
		switch (oType) {
			case 'DateTime':
				metaField = this.findMetaFieldInObject(_configuration.fieldName, this.getMetaObject());
				
				if (metaField && !metaField.precision) {
					metaField.precision = "DATETIME"; // default to datetime if no precision specified in meta object
				}
				
				if (metaField && metaField.precision) {
					switch (metaField.precision) {
						case 'DATETIME':
							_configuration.xtype = _configuration.editable ? 'kdatetime' : 'kdatetimestring';
							break;
						case 'DATE':
							_configuration.xtype = 'kdate';
							break;
						case 'TIME':
							_configuration.xtype = 'ktime';
							break;
						default:
					}
				}
				break;
			default:
		}
	},
	
	loadSubControls: function(_configuration){
		var metaObject = this.getMetaObject();
		var parentOverride = {
			metaComponents: this.metaComponents,			
			row: this.row, // in case we are dealing with a grid form
			objectName: this.objectName,
			formReaderRoot: 'records.' + this.objectName,
			hideLabel: this.labelPosition == 'NONE',
			formLabelStyle: this.formLabelStyle || (this.toBoolAttribute(this.editable) ? '' : 'SHADED'),
			currentItem: this.currentItem,
			pageId: this.pageId,
			dataIslandId: this.dataIslandId,
			maintainFieldCase: this.maintainFieldCase || false
		};
		
		if (this.extensionConfiguration) {
			Ext.apply(parentOverride, Ext.JSON.decode(this.extensionConfiguration));
		}
		
		var visible = true;
		if (_configuration.xtype && _configuration.xtype != this.xtype) {
			// delegate the meta components to all of the subcomponents
			Ext.applyIf(_configuration, parentOverride);
			
			if (_configuration.fieldName) {
				var metaField = this.findMetaFieldInObject(_configuration.fieldName, metaObject);
				if (!metaField || metaField.visible) {
					visible = true;
				}
				else {
					visible = false;
				}
			}
			
			if (visible) {
				this.items.push(_configuration);
			}
		}
		else {
			for (var key in _configuration) {				
				if (Ext.isObject(_configuration[key]) && _configuration[key].xtype && key != 'metaComponents' && key != 'ownerCt') {
					_configuration[key].editable = this.toBoolAttribute(_configuration.editable) ? this.toBoolAttribute(_configuration[key].editable) : this.toBoolAttribute(_configuration.editable);
					_configuration[key].disabled = this.toBoolAttribute(_configuration.disabled) ? this.toBoolAttribute(_configuration.disabled) : this.toBoolAttribute(_configuration[key].disabled);
					_configuration[key].changeOnKeyUp = this.toBoolAttribute(_configuration[key].changeOnKeyUp);
					
					Ext.applyIf(_configuration[key], parentOverride);
					
				    if (key === 'height') {
				        _configuration[key].height = Number(_configuration[key].height)
				    }
    				
				    if (key === 'width') {
				        _configuration[key].width = Number(_configuration[key].width)
				    }					
										
					this.initializeSubControl(_configuration[key], key);
					
					// reset the visible flag to true which is the default condition
					visible = true;
					if (_configuration[key].fieldName) {
						var metaField = this.findMetaFieldInObject(_configuration[key].fieldName, metaObject);
						if (!metaField || metaField.visible) {
							visible = true;
						}
						else {
							visible = false;
						}
					}
					
					if (visible) {
						this.items.push(_configuration[key]);
					}
				}
				else 
					if (Ext.isArray(_configuration[key]) && key != 'metaComponents') {
						for (var a = 0; a < _configuration[key].length; a++) {
							_configuration[key][a].editable = this.toBoolAttribute(_configuration.editable) ? this.toBoolAttribute(_configuration[key][a].editable) : this.toBoolAttribute(_configuration.editable);
							_configuration[key][a].disabled = this.toBoolAttribute(_configuration.disabled) ? this.toBoolAttribute(_configuration.disabled) : this.toBoolAttribute(_configuration[key][a].disabled);
							_configuration[key][a].changeOnKeyUp = this.toBoolAttribute(_configuration[key][a].changeOnKeyUp);
							
							Ext.applyIf(_configuration[key], parentOverride);
							
				            if (key === 'height') {
				                _configuration[key][a].height = Number(_configuration[key][a].height)
				            }
            				
				            if (key === 'width') {
				                _configuration[key][a].width = Number(_configuration[key][a].width)
				            }							
							
							this.initializeSubControl(_configuration[key][a], key);
							
							this.loadSubControls(_configuration[key][a]);
						}
					}
			}
		}
	},
	
	initializeContainerProperties: function(){
		this.border = this.toBoolAttribute(this.border);
		this.collapsible = this.toBoolAttribute(this.collapsible);
		this.collapsed = this.toBoolAttribute(this.collapsed);
		this.scrolling = this.toBoolAttribute(this.scrolling);
		this.editable = this.toBoolAttribute(this.editable);
		this.disabled = this.toBoolAttribute(this.disabled);
		this.autoScroll = this.scrolling;
		
		if (!this.widthUnit) {
			this.widthUnit = 'PCT';
		}
		
		if (this.labelWidth && parseInt(this.labelWidth)) {
			this.labelWidth = parseInt(this.labelWidth);
		}
			
		if (this.width && parseInt(this.width)) {
			this.width = parseInt(this.width);
		}
			
		if (this.height && parseInt(this.height)) {
			this.height = parseInt(this.height);
		}						
	},
	
	applyContainerSize: function(){
		var containerConfiguration = {};
		// calculate width/height/anchor settings
		// width/height for PX are already populated in the control, so if we have PX then don't bother doing anything
		var sizeConfiguration = {};
		
		// if we don't have a pixel width, then we need to set the anchor and remove the width attribute
		if (this.widthUnit != 'PX' && this.width) {
			sizeConfiguration.anchorWidth = this.width;
			delete this.width;
		}
		
		// if we don't have a pixel height, then we need to set the anchor and remove the height attribute
		if (this.heightUnit != 'PX' && this.height) {
			sizeConfiguration.anchorHeight = this.height;
			delete this.height;
		}
		
		// assemble the anchor string if there is any.  This will be auto-applied if any configuration exists
		if (sizeConfiguration.anchorWidth) {
			containerConfiguration.anchor = sizeConfiguration.anchorWidth + (this.widthUnit == 'PCT' ? '%' : '');
		}
		
		if (sizeConfiguration.anchorHeight) {
			// if an anchor height is specified, but no width, then default anchor width
			if (!containerConfiguration.anchor) {
				containerConfiguration.anchor = '-20';
			}
			
			containerConfiguration.anchor += ' ' + sizeConfiguration.anchorHeight + (this.heightUnit == 'PCT' ? '%' : '');
		}
		
		// if no width setting is configured, then we need to set the default
		if (!containerConfiguration.anchor && !this.width) {
			containerConfiguration.anchor = '-20';
		}
			
		if (this.width && parseInt(this.width)) {
			this.width = parseInt(this.width);
		}
			
		if (this.height && parseInt(this.height)) {
			this.height = parseInt(this.height);
		}			
		
		// if we don't have a width then we need to use the default
		Ext.apply(this, containerConfiguration);
	},
	
	applyContainerBorders: function(){
		var metaObject = this.getMetaObject();
		this.collapsible = this.toBoolAttribute(this.collapsible);
		this.border = this.toBoolAttribute(this.border);
		
		// trim the title
		if (this.title) {
			this.title = this.title.replace(/^\s+|\s+$/, '');
		}
		
		if (!Ext.isEmpty(this.title)) {
			this.border = true;
		}
		
		this.bodyBorder = this.border;		
	},
	
	applyFieldSize: function() {
        var defaultLabelWidth = 100;
        
        if (this.width && this.widthUnit === 'PX' && !this.labelWidth) {
            this.width = defaultLabelWidth + this.width;            
        }
        else if (this.labelWidth && this.widthUnit === 'PX') {
            this.width = this.labelWidth + this.width;
        }	
	},
	
	/**
	 * Returns the particular meta object for a particular meta object name.
	 * @param {String} _objectName The name of the meta object
	 * @param {Object} _metaComponents the collection of meta components to search through.
	 * @return {Object} the particular meta object if found.  null is returned if not found.
	 */
	findMetaObject: function(_objectName, _metaComponents){
		if (_objectName && _metaComponents) {
			for (var i = 0; i < _metaComponents.length; i++) {
				if (_metaComponents[i].MetaObjectType && _metaComponents[i].MetaObjectType.name == _objectName) {
					return _metaComponents[i].MetaObjectType;
				}
			}
		}
		
		return null;
	},
	
	getMetaObject: function(){
		return this.findMetaObject(this.objectName, this.metaComponents);
	},
	
	/**
	 * Returns the particular meta field within a particular meta object.
	 * @param {String} _objectName The name of the meta object
	 * @param {String} _fieldName The name of the meta field
	 * @param {Object} _metaComponents the collection of meta components to search through.
	 * @return {Object} the particular meta field if found.  null is returned if not found.
	 */
	findMetaField: function(_objectName, _fieldName, _metaComponents){
		var metaObject = this.findMetaObject(_objectName, _metaComponents);
		if (metaObject) {
			return this.findMetaFieldInObject(_fieldName, metaObject);
		}
		return {};
	},
	
	findMetaFieldInObject: function(_fieldName, _metaObject){
		if (_fieldName) {
			for (var key in _metaObject) {
				if (Ext.isArray(_metaObject[key])) {
					for (var i = 0; i < _metaObject[key].length; i++) {
						if (key.endsWith('Field') && _metaObject[key][i].name && _metaObject[key][i].name.toLowerCase() == _fieldName.toLowerCase()) {
							return _metaObject[key][i];
						}
					}
				}
				else if (key.endsWith('Field') && Ext.isObject(_metaObject[key]) && _metaObject[key].name && _metaObject[key].name.toLowerCase() == _fieldName.toLowerCase()) {
					return _metaObject[key];
				}
			}
		}
		return {};
	},
	
	getDimensionCount: function(){
		var metaObject = this.getMetaObject();
		var dimCount = 0;
		for (var key in metaObject) {
			if (Ext.isArray(metaObject[key])) {
				for (var i = 0; i < metaObject[key].length; i++) {
					if (key.endsWith('Field') && metaObject[key][i].dimensionable) {
						dimCount++;
					}
				}
			}
			else if (key.endsWith('Field') && metaObject[key].dimensionable) {
				dimCount++;
			}
		}
		
		return dimCount;
	},
	
	getMetaField: function(){
		return this.findMetaField(this.objectName, this.fieldName, this.metaComponents);
	},
	
	getColorField: function(){
		return this.findColorMetaField(this.getMetaObject());
	},
	
	getKeyField: function(){
		return this.findKeyMetaField(this.getMetaObject());
	},
	
	isActionContainer: function(){
		// determines if the widget is embedded within an action.
		// kerbe: we will use the pageId which starts with a known prefix for actions.
		return (this.pageId && this.pageId.indexOf('k') == 0);
	},
	
	applyMetaField: function(){
		this.pageId = this.pageId;
		
		var metaObject = this.getMetaObject();
		if (metaObject) {
			this.formReaderRoot = metaObject.path;
			// TODO: this should be a global issue.  in FieldChanged we are using initialConfig.formReaderRoot
			this.initialConfig.formReaderRoot = this.formReaderRoot;
		}
		
		var field = this.getMetaField();
		var hidden = this.toBoolAttribute(field.hidden);
		if (field) {
			// convert field configuration attributes to ext configuration attributes
			
			// configure the fieldName to sometimes be case sensitive
			var fieldName = '';
			if (field.name) {
				fieldName = field.name;
			}
				
			if (!this.maintainFieldCase) {
				fieldName = fieldName.toLowerCase();
			}
			
			var fieldConfiguration = {
				name: fieldName,
				fieldLabel: ((hidden || !field.caption) ? '' : (field.caption + (field.required ? '*' : '') || '')),
				readOnly: !this.editable,
				//fieldClass: this.editable ? '' : Ext.baseCSSPrefix + 'field-kaseya-noborder',
				formItemCls: this.formLabelStyle == 'SHADED' ? Ext.baseCSSPrefix + 'field-item-kaseya-view' : '',
				labelStyle: this.formLabelStyle == 'SHADED' ? 'background-color: #f2f4f7;' : '',
				allowBlank: !field.required,
				regex: field.regex ? new RegExp(field.regex) : null
            };
			
			// do not allow tabbing into read-only or disabled fields
			if (fieldConfiguration.readOnly || this.disabled) {
			    fieldConfiguration.tabIndex = "-1";
			}
			
			Ext.applyIf(fieldConfiguration, field);
			
			// calculate width/height/anchor settings
			// width/height for PX are already populated in the control, so if we have PX then don't bother doing anything
			var sizeConfiguration = {};
			
			// if we don't have a pixel width, then we need to set the anchor and remove the width attribute
			if (this.widthUnit != 'PX' && this.width) {
				sizeConfiguration.anchorWidth = this.width;
				delete this.width;
			}
			
			if (!this.heightUnit && this.height) {
				this.heightUnit = 'PX';
			}
			
			// if we don't have a pixel height, then we need to set the anchor and remove the height attribute
			if (this.heightUnit != 'PX' && this.height) {
				sizeConfiguration.anchorHeight = this.height;
				
				// if we have no anchor width, then force one
				if (!sizeConfiguration.anchorWidth) {
					this.widthUnit = 'PX';
					sizeConfiguration.anchorWidth = -24;
				}
				
				delete this.height;
			}
			
			// assemble the anchor string if there is any.  This will be auto-applied if any configuration exists
			if (sizeConfiguration.anchorWidth) {
				fieldConfiguration.anchor = sizeConfiguration.anchorWidth + (this.widthUnit == 'PCT' ? '%' : '');
				
				if (sizeConfiguration.anchorHeight) {
					fieldConfiguration.anchor += ' ' + sizeConfiguration.anchorHeight + (this.heightUnit == 'PCT' ? '%' : '');
				}
			}
			
			// if we don't have a width then we need to use the default
			if (!fieldConfiguration.anchor && !this.width) {
				fieldConfiguration.anchor = '-24';
			}
			
			// configure on change controller configuration
			if (this.ControllerReference && this.ControllerReference.name) {
				this.initialConfig.dataObjectName = this.objectName;
				this.initialConfig.referringWebWindowId = WEB_WINDOW_ID;
				this.initialConfig.controllerConfiguration = this.findController(this.ControllerReference.name);
				if (this.initialConfig.controllerConfiguration) {
					this.initialConfig.website = this.initialConfig.controllerConfiguration.website;
				}
			}
			
			Ext.apply(this, fieldConfiguration);
		}
	},
	
	findColorMetaField: function(_metaObject){
		for (var t in _metaObject) {
			if (Ext.isObject(_metaObject[t]) && t == 'ColorField') {
				return _metaObject[t];
			}
		}
		
		return null;
	},
	
	findKeyMetaField: function(_metaObject){
		for (var t in _metaObject) {
			if (Ext.isObject(_metaObject[t]) && t == 'KeyField') {
				return _metaObject[t];
			}
		}
		
		return null;
	},
	
	populateJSonReaderColumnArray: function(_array, _obj, _addObject){
		if (_addObject) {
			var name = '';			
			if (_obj.name) {
				name = _obj.name;
			}
			
			if (!this.maintainFieldCase) {
				name = name.toLowerCase();
			}
			
			_array.push({
				name: name,
				mapping: name
			});
		}
		else {
			for (var t in _obj) {
				if (Ext.isArray(_obj[t]) && t.endsWith('Field')) {
					for (var a = 0; a < _obj[t].length; a++) {
						this.populateJSonReaderColumnArray(_array, _obj[t][a], true);
					}
				}
				else 
					if (Ext.isObject(_obj[t]) && t.endsWith('Field')) {
						var name = '';
						if (_obj[t].name) {
							name = _obj[t].name;
						}
						
						if (!this.maintainFieldCase) {
							name = name.toLowerCase();
						}
						
						_array.push({
							name: name,
							mapping: name
						});
					}
			}
		}
	},
	
	buildValidValueArray: function(_fieldValidValues){
		var x = [];
		if (_fieldValidValues) {
			for (var i = 0; i < _fieldValidValues.length; i++) {
				x.push([_fieldValidValues[i].value, _fieldValidValues[i].name.replace(/&#39;/g, "'")]);
			}
		}
		
		return x;
	},
	
	findFilterSpecification: function(_fieldName){
		var metaObject = this.getMetaObject();
		if (metaObject && metaObject.FilterSpecification) {
			if (Ext.isArray(metaObject.FilterSpecification)) {
				for (var a = 0; a < metaObject.FilterSpecification.length; a++) {
					if (metaObject.FilterSpecification[a].fieldName.toLowerCase() == _fieldName.toLowerCase()) {
						return metaObject.FilterSpecification[a];
					}
				}
			}
			else if (Ext.isObject(metaObject.FilterSpecification)) {
				if (metaObject.FilterSpecification.fieldName.toLowerCase() == _fieldName.toLowerCase()) {
					return metaObject.FilterSpecification;
				}
			}
		}
		
		return null;
	},
	
	getControllerToolbar: function(_pageId, _dataIslandId, _dataObjectName, _force, _valign, _customControllers){
		var controllers = this.getControllers(this.ControllerReference, _pageId, _dataIslandId, _dataObjectName, _force, _valign);
		
		// only count visible controllers.  If there are only hidden controllers, then we don't need to show the blank bar
		var visibleControllerCount = 0;
		for (var i = 0; i < controllers.length; i++) {
			if (!controllers[i].hidden) {
				visibleControllerCount++;
			}
		}
		
		// process any sub-interaction controllers (menus)
		var metaObject = this.getMetaObject() || this.metaComponents;
		var interactionControllers = metaObject.MetaObjectType ? metaObject.MetaObjectType.InteractionController : metaObject.InteractionController;
		
		if (interactionControllers) {
		    if (Ext.isObject(interactionControllers)) {
		        interactionControllers = [].concat(interactionControllers);
		    };
    		
		    Ext.each(interactionControllers, function(meta, i){								    
			    if (this.toBoolAttribute(meta.menuContainer) && meta.SubInteractionController) {             
				    var m = new Ext.menu.Menu({
					    id: 'subMenuItem' + meta.name + '' + i
				    });
    				
				    var subControllers = [];
    				
				    // find the matching controller instances by name (we only have meta info), add menu items
				    Ext.each(meta.SubInteractionController, function(sc){
					    Ext.each(controllers, function(c){
						    if (c.name == sc.name) {
							    subControllers.push(c); // keep track of modified subcontrollers
    							
							    if (sc.separator && sc.separator === 'top') {
								    m.add(new Ext.menu.Separator());
							    }
    							
							    m.add(c);
    							
							    if (sc.separator && sc.separator === 'bottom') {
								    m.add(new Ext.menu.Separator());
							    }
						    }
					    }, this);
	                }, this);	                
	                   				   				
				    // remove old top-level controllers since they will be added to this menu instead
				    Ext.each(subControllers, function(sc){
					    //controllers.remove(sc);
                        Ext.Array.remove(controllers, sc);
					});

					var tbButton = {
					    text: meta.caption,
					    icon: AdjustSSLPath('/vsaPres/App_Themes/0/images/size16/actions/' + meta.image + '.png'),
					    menu: m,
					    initialEnabledState: true // enable the menu by default
					};	                
    				
			        // inject the menu at the correct location
			        controllers.splice(i, 0, tbButton);
			    }
			}, this);    
		}
		
		if (_customControllers && _customControllers.length > 0) {
			// if allowRefresh is configured, then add a refresh button to reload the data store associated with this toolbar's owning object
			if (_customControllers && _customControllers.length > 0) {
				for (var i = 0; i < _customControllers.length; i++) {
					this.addToolbarController(controllers, _customControllers[i], {
						hidden: false
					}, _pageId, _dataIslandId, _dataObjectName, true, (!_valign || controllerRef.controllerVAlign.toLowerCase() == 'top' ? true : false));
					
					visibleControllerCount++;
				}
			}
		}
		
		if (controllers.length > 0 || _force) {
			var hidden = visibleControllerCount <= 0;
			if (_force) { 
				hidden = false;
			}
			
			var hidden = visibleControllerCount <= 0;
			if (_force) {
				hidden = false;
			}
			
			var toolbar = Ext.create('Ext.toolbar.Toolbar', {
				height: 24,
				hidden: hidden,
				items: controllers
			});
			
			return toolbar;
		}
		
		return null;
	},
	
	getControllerMenu: function(_pageId, _dataIslandId, _dataObjectName, _force){
		var controllers = this.getControllers(this.mRefs, _pageId, _dataIslandId, _dataObjectName, _force);
		if (controllers.length > 0 || _force) {
			var toolbar = Ext.create('Ext.menu.Menu', {
				items: controllers
			});
			
			return toolbar;
		}
		
		return null;
	},
	
	getControllers: function(refs, _pageId, _dataIslandId, _dataObjectName, _force, _valign){
		var controllers = [];
		var controller;
		var controllerRef;
		if (refs) {
			if (Ext.isArray(refs)) {
				// for each reference, build the controller and push it to controllers
				for (var i = 0; i < refs.length; i++) {
					controllerRef = refs[i];
					// if there is no value specified for the controllerVAlign, default to TOP
					if (!controllerRef.controllerVAlign) {
						controllerRef.controllerVAlign = 'TOP';
					}
					
					if (!_valign || _valign.toLowerCase() == controllerRef.controllerVAlign.toLowerCase()) { // if no valign was provided, or the controllerVAlign is configured as TOP, then display the icon, otherwise, do not display the icon
						controller = this.findController(controllerRef.name);
						this.addToolbarController(controllers, controller, controllerRef, _pageId, _dataIslandId, _dataObjectName, _force, (!_valign || controllerRef.controllerVAlign.toLowerCase() == 'top' ? true : false));
					}
				}
			}
			else if (Ext.isObject(refs)) {
				controllerRef = refs;
				
				// if there is no value specified for the controllerVAlign, default to TOP
				if (!controllerRef.controllerVAlign) {
					controllerRef.controllerVAlign = 'TOP';
				}
				
				if (!_valign || _valign.toLowerCase() == controllerRef.controllerVAlign.toLowerCase()) {
					controller = this.findController(controllerRef.name);
					this.addToolbarController(controllers, controller, controllerRef, _pageId, _dataIslandId, _dataObjectName, _force, (!_valign || controllerRef.controllerVAlign.toLowerCase() == 'top' ? true : false));
				}
			}
		}
		
		return controllers;
	},
	
	addToolbarController: function(controllers, controller, controllerRef, _pageId, _dataIslandId, _dataObjectName, _force, _showIcon){
		if (controller) {
			if (controller.behavior == '!START') {
				controller.behavior = 'START';
			}
			
			var disabled = false;
			if (this.toBoolAttribute(controller.enabled)) {
				if (_force === true) {
					disabled = false;
				}
				else {
					disabled = controller.minSelectionRequirement && controller.minSelectionRequirement.indexOf('0') === -1;
				}
			}
			else {
				disabled = true;
			}
			
			var toolbarController = {
				referringWebWindowId: WEB_WINDOW_ID,
				pageId: _pageId,
				dataIslandId: _dataIslandId,
				dataObjectName: _dataObjectName,
				initialEnabledState: this.toBoolAttribute(controller.enabled),
				controllerConfiguration: controller,
				hidden: this.toBoolAttribute(controllerRef.hidden),
				disabled: disabled,
				isDefault: this.toBoolAttribute(controller.isDefault),
				isToggle: this.toBoolAttribute(controller.isToggle),
				rightClickFlag: this.toBoolAttribute(controller.rightClickFlag),
				text: controller.caption,
				tooltip: {
					text: (controller.tooltip ? ((Ext.isObject(controller.tooltip) && controller.tooltip.text) ? controller.tooltip.text : controller.tooltip) : controller.caption),
					autoHide: true
				},
				handler: (controller.handler ? controller.handler : ControllerClickHandler),
				validate: this.toBoolAttribute(controller.validate)
			};
			
			Ext.applyIf(toolbarController, controllerRef);
			Ext.applyIf(toolbarController, controller);
			
			if (controller.PreController) {
				if (controller.PreController.PreControllerYesNo) {
					toolbarController.controllerConfiguration.PreController.type = 'PreControllerYesNo';
					Ext.apply(toolbarController.controllerConfiguration.PreController, controller.PreController.PreControllerYesNo);
				}
				else if (controller.PreController.PreControllerYesNoCancel) {
					toolbarController.controllerConfiguration.PreController.type = 'PreControllerYesNoCancel';
					Ext.apply(toolbarController.controllerConfiguration.PreController, controller.PreController.PreControllerYesNoCancel);
				}
				else if (controller.PreController.PreControllerPrompt) {
					toolbarController.controllerConfiguration.PreController.type = 'PreControllerPrompt';
					Ext.apply(toolbarController.controllerConfiguration.PreController, controller.PreController.PreControllerPrompt);
				}
				else if (controller.PreController.PreControllerAlert) {
					toolbarController.controllerConfiguration.PreController.type = 'PreControllerAlert';
					Ext.apply(toolbarController.controllerConfiguration.PreController, controller.PreController.PreControllerAlert);
				}
			}
			
			if (_showIcon) {
				toolbarController.cls = Ext.baseCSSPrefix + 'btn-text-icon';
				var iconImageName = controller.image ? controller.image : 'action';
				toolbarController.icon = AdjustSSLPath('/vsaPres/App_Themes/0/images/size16/actions/' + iconImageName + '.png');
			}
			
			if (controller.delay) {
				toolbarController.handler = function(_btn) {
					Ext.defer(ControllerClickHandler, _btn.delay, this, [_btn])
				}
			}
			
			controllers.push(toolbarController);
		}
	},
	
	findController: function(_name, _metaObject, forceMenuContainer){
		var metaObject = _metaObject || this.getMetaObject() || this.metaComponents;
		if (Ext.isArray(metaObject)) {
			for (var i = 0; i < metaObject.length; i++) {
				var ref = this.findController(_name, metaObject[i]);
				if (ref) {
					return ref;
				}
			}
		}
		else {
			var foundController = null;
			
			var controllers = metaObject.MetaObjectType ? metaObject.MetaObjectType.InteractionController : metaObject.InteractionController;			
			if (controllers) {
				if (Ext.isArray(controllers)) {
				    for (var i = 0; i < controllers.length; i++) {			        
				        var ic = controllers[i];
				        
				        if (forceMenuContainer) {
				            ic.menuContainer = true;
				        }				        
				        
				        if (ic.name == _name && !this.toBoolAttribute(ic.menuContainer)) {
				            foundController = ic;
				        }
				        
				        if (this.toBoolAttribute(ic.menuContainer) && ic.SubInteractionController) {
				                if (!Ext.isArray(ic.SubInteractionController)) {
				                    ic.SubInteractionController = [ic.SubInteractionController];
				                }
				                
				                Ext.each(ic.SubInteractionController, function(sub) {
				                    if (sub.name == _name) {
				                        sub.menuContainer = ic;
				                        foundController = sub;
				                        return false;
				                    }
				                }, this);
				            }
				        }
				}
				else {
			        if (forceMenuContainer) {
			            controllers.menuContainer = true;
			        }				    
				    
				    if (controllers.name == _name && !this.toBoolAttribute(controllers.menuContainer)) {
			            foundController = controllers;
			        }
			        
			        if (this.toBoolAttribute(controllers.menuContainer) && controllers.SubInteractionController) {		        
			            if (!Ext.isArray(controllers.SubInteractionController)) {
			                controllers.SubInteractionController = [].concat(controllers.SubInteractionController);
					    }						
						
					    Ext.each(controllers.SubInteractionController, function(sub) {
			                if (sub.name == _name) {
			                    sub.menuContainer = controllers;
			                    foundController = sub;
			                    return false;
			                }
			            }, this);
			        };			       
	            }
				
				if (foundController) {
					foundController.website = ((foundController.host == 'CLASSIC_WINDOW' || foundController.host == 'SINGLE_USE_CLASSIC_WINDOW') ? CLASSIC_ACTION_URL : ACTION_URL);
				}
			}
			
			// if the controller was not found among the interaction controllers, then look in the url controllers
			if (!foundController) {
				controllers = metaObject.MetaObjectType ? metaObject.MetaObjectType.UrlController : metaObject.UrlController;
				if (controllers) {
					if (Ext.isArray(controllers)) {
						for (var i = 0; i < controllers.length; i++) {
							if (controllers[i].name == _name) {
								foundController = controllers[i];
							}
						}
					}
					else {
						if (controllers.name == _name) {
							foundController = controllers;
						}
					}
					
					if (foundController) {
						foundController.website = URL_ACTION;
						// if this is a UrlController, it wont have a behavior.  These are exclusively START controllers.
						foundController.behavior = 'START';
						foundController.includeMarkedObjects = this.toBoolAttribute(foundController.includeMarkedObjects);
						foundController.includeWindowId = this.toBoolAttribute(foundController.includeWindowId);
						foundController.useLinkId = this.toBoolAttribute(foundController.useLinkId);
					}
				}
			}
			
			if (foundController) {
				foundController.rightClickFlag = this.toBoolAttribute(foundController.rightClickFlag);
				foundController.displayToolbarText = this.toBoolAttribute(foundController.displayToolbarText);
				foundController.enabled = this.toBoolAttribute(foundController.enabled);
				foundController.isDefault = this.toBoolAttribute(foundController.isDefault);
				foundController.linked = this.toBoolAttribute(foundController.linked);
				foundController.suppressProcessIndicator = this.toBoolAttribute(foundController.suppressProcessIndicator);
				foundController.isToggle = this.toBoolAttribute(foundController.isToggle);
				foundController.menuContainer = this.toBoolAttribute(foundController.menuContainer);
            }						
			
			return foundController;
		}
		
		return null;
	},
	
	/**
	 * Returns the boolean representation of the param.  This will do things like convert the string value to a boolean, and if undefined, set to false.
	 * @param {Object} _value the current value of the parameter.
	 * @return {Object} the boolean representation of the value provided.
	 */
	toBoolAttribute: function(_value){
		var result = false;
		if (Ext.isBoolean(_value)) {
			result = _value;
		}
		else if (Ext.isString(_value)) {
			if (_value && _value.toLowerCase() == 'true') {
				result = true;
			}
		}
		
		return result;
	}
	
});

function setMonthNumbers(langId) {
    if (langId == 'zh-CN') {
        Date.monthNumbers = {
            "一月": 0,
            "二月": 1,
            "三月": 2,
            "四月": 3,
            "五月": 4,
            "六月": 5,
            "七月": 6,
            "八月": 7,
            "九月": 8,
            "十月": 9,
            "十一月": 10,
            "十二月": 11
        };
    }
}

Ext.util.Format.kaseyaFileSize = function(size){
	if (size < 1024) {
		return size + " bytes";
	}
	else if (size < 1048576) {
		return (Math.round(((size * 10) / 1024)) / 10) + " KB";
	}
	else if (size < 1073741824) {
		return (Math.round(((size * 10) / 1048576)) / 10) + " MB";
	}
	else if (size < 1099511627776) {
		return (Math.round(((size * 10) / 1073741824)) / 10) + " GB";
	}
	else {
		return (Math.round(((size * 10) / 1099511627776)) / 10) + " TB";
	}
}

// IE does not have the filter function natively, so add it manually
if (!('filter' in Array.prototype) && Ext.isIE) {
    Array.prototype.filter= function(filter, that /*opt*/) {
        var other= [], v;
        for (var i=0, n= this.length; i<n; i++) {
            if (i in this && filter.call(that, v= this[i], i, this)) {
                other.push(v);
            }
        }
                
        return other;
    };
}